﻿/*
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 3 of the License, or
   (at your option) any later version.
  
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
 
   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software Foundation,
   Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
*/

using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text;
using System.Management;

namespace BHInjector
{
    public delegate void DiabloDetectedEventHandler(int processId);

    public class DiabloDetector : ManagementEventWatcher
    {
        private const string ProcessEventQuery = @"SELECT * FROM __InstanceOperationEvent WITHIN 1 WHERE TargetInstance ISA 'Win32_Process' and TargetInstance.Name = '" + DiabloProcessName + ".exe'";
        private const string DiabloProcessName = @"Game";
        private const string DiabloClassName = @"Diablo II";

        private readonly Logger logger;

        public DiabloDetector(Logger logger)
        {
            this.logger = logger;
            Query.QueryLanguage = "WQL";
            Query.QueryString = ProcessEventQuery;
            EventArrived += ProcessEventArrived;
        }

        public new void Start()
        {
            base.Start();
            logger.Log("D", "Diablo detector started.");

            var processes = Process.GetProcessesByName(DiabloProcessName);
            foreach (var process in processes)
            {
                DiabloDetected(process.Id);
            }
        }

        public event DiabloDetectedEventHandler OnDiabloDetected;

        private void ProcessEventArrived(object sender, EventArrivedEventArgs e)
        {
            if (e.NewEvent.ClassPath.ClassName != "__InstanceCreationEvent") 
                return;

            var process = GetCreatedProcess(e);

            if (GetProcessMainWindowClassName(process) == DiabloClassName)
            {
                DiabloDetected(process.Id);
            }
        }

        private void DiabloDetected(int processId)
        {
            logger.Log("D", "Diablo 2 detected (pid: {0}).", processId);
            OnDiabloDetected(processId);
        }

        protected override void Dispose(bool disposing)
        {
            Stop();
            base.Dispose(disposing);
        }

        #region Helper methods
        private static Process GetCreatedProcess(EventArrivedEventArgs e)
        {
            var processId = Int32.Parse(((ManagementBaseObject)(e.NewEvent["TargetInstance"])).Properties["Handle"].Value.ToString());

            return Process.GetProcessById(processId);
        }

        [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        private static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);

        private static string GetProcessMainWindowClassName(Process process)
        {
            process.WaitForInputIdle();
            var mainWindow = process.MainWindowHandle;

            StringBuilder className = new StringBuilder(32);
            //Get the window class name
            GetClassName(mainWindow, className, className.Capacity);

            return className.ToString();
        }
        #endregion
    }
}
